package cyan.gf.controller;


import com.sun.javafx.scene.control.skin.ContextMenuContent;
import cyan.gf.control.MessageBox;
import cyan.gf.control.SimpleTreeItem;
import cyan.gf.data.ControllerDataInitializable;
import cyan.gf.db.*;
import cyan.gf.field.FieldWarpper;
import cyan.gf.field.MySQLFieldWarpper;
import cyan.gf.field.OracleFieldWarpper;
import cyan.gf.sql.MySQLDao;
import cyan.gf.sql.OracleDao;
import cyan.gf.tree.TreeLeaf;
import cyan.gf.util.PropReader;
import cyan.gf.util.StagePool;
import cyan.gf.util.StageUtil;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.text.Text;
import javafx.stage.Modality;
import javafx.stage.Stage;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.net.URL;
import java.util.*;

public class InitializerController implements Initializable {

  protected static Logger logger = LogManager.getLogger(InitializerController.class);

  protected static String CURRENT_STAGE_ID = "main";

  private JdbcProp jdbcProp;
  private boolean mListener = false;
  @FXML
  private ComboBox<Schema> dbTypeComboBox;
  @FXML
  private TreeView<SimpleTreeItem> dbTree;
  @FXML
  protected Button connButton;
  @FXML
  private TextField addressField, userNameField, passowrdField;
  private TreeItem<SimpleTreeItem> dbTreeRootItem;
  private List<TableField> tableFields;
  private String currentSchemaName, currentTableName;
  final ContextMenu tableContextMenu = new ContextMenu();
  private static String defaultAddress = "127.0.0.1:3306";//"jdbc:mysql://127.0.0.1:3306/";
  private static String defaultJDBCUserName = "root";
  private static String defaultPassword = "123456";
  CrudInstance crudInstance;
  MySQLDao mySQLDao;
  OracleDao oracleDao;
  Schema schema;
  FieldWarpper fieldWarpper;
  List<TreeItem<SimpleTreeItem>> treeItems;

  @Override
  public void initialize(URL location, ResourceBundle resources) {
    initComponent();
    initMenus();
  }

  private void initTreeRoot(String rootName) {

    if (dbTree.getRoot() != null && dbTree.getRoot().getChildren().size() > 0) {
      dbTree.getRoot().getChildren().clear();
    }
    SimpleTreeItem simpleTreeItem = new SimpleTreeItem(rootName, "/images/tree_root.png");
    simpleTreeItem.setMessage(TreeLeaf.ROOT);
    dbTreeRootItem = new TreeItem<>(simpleTreeItem);
    dbTree.setRoot(dbTreeRootItem);
  }

  private void loadTables(TreeItem<SimpleTreeItem> clickItem) {
    dbTree.setCursor(Cursor.WAIT);
    String schemaName = clickItem.getValue().getLabelText();
    new Thread(() -> {
      if (schema == Schema.MYSQL) {
        treeItems = mySQLDao.loadTables(schemaName);
      }
      if (schema == Schema.ORACLE) {
        treeItems = oracleDao.loadTables();
      }
      Platform.runLater(() -> {
        clickItem.getChildren().addAll(treeItems);
        clickItem.setExpanded(true);
        dbTree.setCursor(Cursor.DEFAULT);
      });
    }).start();
  }

  private void loadColumn(TreeItem<SimpleTreeItem> currentTableItem) {
    dbTree.setCursor(Cursor.WAIT);
    tableFields = new ArrayList<>();
    if (schema == Schema.MYSQL) {
      loadMySQLColumn(currentTableItem);
    }
    if (schema == Schema.ORACLE) {
      loadOracleColumn(currentTableItem);
    }
  }

  private void loadMySQLColumn(TreeItem<SimpleTreeItem> currentTableItem) {
    new Thread(() -> {
      tableFields = mySQLDao.getTableFields(currentSchemaName, currentTableName);
      for (TableField field : tableFields) {
        String pngPath = "/images/tree_column.png";
        String nullable = "";
        String extra = "";
        if (field.getKey() == "Id") {
          pngPath = "/images/column_key.png";
        }
        if (field.getNullable() == "true") {
          nullable = " | " + "NotNull";
        }
        if (field.getExtra() == Column.auto_increment) {
          extra = " | " + "auto_increment";
        }
        SimpleTreeItem simpleTreeItem = new SimpleTreeItem(field.getFieldText() + " | " + field.getTypeText() + nullable + extra, pngPath);
        TreeItem<SimpleTreeItem> item = new TreeItem<>(simpleTreeItem);
        Platform.runLater(() -> currentTableItem.getChildren().add(item));
      }
      Platform.runLater(() -> {
        currentTableItem.setExpanded(true);
        dbTree.setCursor(Cursor.DEFAULT);
      });
    }).start();
  }

  private void loadOracleColumn(TreeItem<SimpleTreeItem> currentTableItem) {
    new Thread(() -> {
      tableFields = oracleDao.getTableFields(currentTableName);
      for (TableField field : tableFields) {
        String pngPath = "/images/tree_column.png";
        String nullable = "";
        if (field.getKey() == "Id") {
          pngPath = "/images/column_key.png";
        }
        if (field.getNullable() == "true") {
          nullable = " | " + "NotNull";
        }
        SimpleTreeItem simpleTreeItem = new SimpleTreeItem(field.getFieldText() + " | " + field.getTypeText() + nullable, pngPath);
        TreeItem<SimpleTreeItem> item = new TreeItem<>(simpleTreeItem);
        Platform.runLater(() -> currentTableItem.getChildren().add(item));
      }
      Platform.runLater(() -> {
        currentTableItem.setExpanded(true);
        dbTree.setCursor(Cursor.DEFAULT);
      });
    }).start();
  }

  private void initComponent() {
    ObservableList<Schema> dbTypeList = FXCollections.observableArrayList();
    dbTypeList.add(0, Schema.MYSQL);
    dbTypeList.add(1, Schema.ORACLE);
    dbTypeComboBox.setItems(dbTypeList);
    dbTypeComboBox.getSelectionModel().select(0);
    addressField.setText(defaultAddress);
    userNameField.setText(defaultJDBCUserName);
    passowrdField.setText(defaultPassword);
    connButton.setGraphic(new ImageView("/images/start.png"));
    dbTypeComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
      if (newValue == Schema.MYSQL) {
        addressField.setText(defaultAddress);
      }
      if (newValue == Schema.ORACLE) {
        addressField.setText("127.0.0.1:1521:XE");
      }
    });
    if (!mListener) {
      dbTree.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent e) -> {
        Node node = (Node) e.getTarget();
        if ((node instanceof Text || node instanceof ImageView || node instanceof SimpleTreeItem
            || node instanceof TreeCell) && dbTree.getRoot() != null
            && dbTree.getRoot().getChildren().size() > 0) {
          TreeItem<SimpleTreeItem> clickItem = dbTree.getSelectionModel().getSelectedItem();
          if (clickItem == null) {
            return;
          }
          TreeLeaf clickMessage = clickItem.getValue().getMessage();
          if (e.getButton() == MouseButton.PRIMARY && e.getClickCount() == 2) {
            if (clickItem.getChildren().size() == 0) {
              if (clickMessage == TreeLeaf.SCHEMA) {
                currentSchemaName = clickItem.getValue().getLabelText();
                loadTables(clickItem);
              } else if (clickMessage == TreeLeaf.TABLE) {
                currentTableName = clickItem.getValue().getLabelText();
                loadColumn(clickItem);
              }
            }
          } else if (e.getButton() == MouseButton.SECONDARY) {
            if (clickMessage == TreeLeaf.SCHEMA) {
              tableContextMenu.show(dbTree, e.getScreenX(), e.getScreenY());
            } else if (e.getButton() == MouseButton.PRIMARY) {
              tableContextMenu.hide();
            }
          }
        }
      });
    }
    mListener = true;
  }

  private void initMenus() {
    MenuItem generateModelItem = new MenuItem("Generate Java Model");
    tableContextMenu.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
      if (event.getTarget() instanceof ContextMenuContent)
        tableContextMenu.hide();
    });
    generateModelItem.setOnAction(event -> {
      List<String> tableNames = new ArrayList<>();
      TreeItem clickItem = dbTree.getSelectionModel().getSelectedItem();
      ObservableList<TreeItem<SimpleTreeItem>> children = clickItem.getChildren();
      if (children.size() == 0) {
        MessageBox.warn(CURRENT_STAGE_ID, "Please double click to initialize the database.");
        return;
      }
      children.forEach(treeItem -> tableNames.add(treeItem.getValue().getLabelText()));

      String resource = "/fxml/generateModel.fxml";
      StageUtil stageUtil = new StageUtil();
      Stage stage = stageUtil.initStage(resource, 790, 661, "Generate Java Model");
      stage.initOwner(StagePool.get("main"));
      stage.initModality(Modality.WINDOW_MODAL);
      stage.setResizable(false);
      ControllerDataInitializable gc = stageUtil.getLoader().getController();
      Map dataMap = new HashMap();
      dataMap.put("currentSchemaName", currentSchemaName);
      dataMap.put("tableNames", tableNames);
      dataMap.put("crudInstance", crudInstance);
      dataMap.put("schema", schema);
      gc.initData(dataMap);
      StagePool.put("generator", stage);
      stage.show();
    });
    tableContextMenu.getItems().add(generateModelItem);
  }

  public void initConn() {
    if (DBHelper.isClosed()) {
      initJdbc();
      if (crudInstance == null) {
        return;
      }
      initTreeRoot(jdbcProp.getUrl());
      if (schema == Schema.ORACLE) {
        SimpleTreeItem simpleTreeItem = new SimpleTreeItem(jdbcProp.getUserName().toUpperCase(), "/images/tree_schema.png");
        simpleTreeItem.setMessage(TreeLeaf.SCHEMA);
        dbTreeRootItem.getChildren().add(new TreeItem<>(simpleTreeItem));
      }
      if (schema == Schema.MYSQL) {
        dbTreeRootItem.getChildren().addAll(mySQLDao.listSchema());
      }
      connButton.setGraphic(new ImageView("/images/stop.png"));
      addressField.setDisable(true);
      userNameField.setDisable(true);
      passowrdField.setDisable(true);
      dbTypeComboBox.setDisable(true);
    } else {
      connButton.setGraphic(new ImageView("/images/start.png"));
      addressField.setDisable(false);
      userNameField.setDisable(false);
      passowrdField.setDisable(false);
      dbTypeComboBox.setDisable(false);
      dbTree.setRoot(null);
      DBHelper.close();
      jdbcProp = null;
    }
  }

  public void about() {
    PropReader.load("/config/config.properties");
    MessageBox.showResult(StagePool.get("main"), Alert.AlertType.INFORMATION, "About",
        String.format("%s (%s)", PropReader.getString("app.title"), PropReader.getString("app.version")) +
            "\nMySQL支持版本：5.x\nOracle支持版本：10.x,11.x\n此版本为学习测试版本，如果你有好的意见或者建议，请联系我QQ:331855843");
  }

  private void initJdbc() {
    jdbcProp = new JdbcProp();
    switch (dbTypeComboBox.getValue()) {
      case MYSQL:
        schema = Schema.MYSQL;
        fieldWarpper = new MySQLFieldWarpper();
        jdbcProp.setDriverName(JdbcDriver.MYSQL_DRIVER);
        jdbcProp.setUrl(String.format("jdbc:mysql://%s/", addressField.getText().trim()));
        break;
      case ORACLE:
        schema = Schema.ORACLE;
        fieldWarpper = new OracleFieldWarpper();
        jdbcProp.setDriverName(JdbcDriver.ORACLE_DRIVER);
        jdbcProp.setUrl(String.format("jdbc:oracle:thin:@%s", addressField.getText().trim()));
    }

    jdbcProp.setUserName(userNameField.getText().trim());
    jdbcProp.setPassword(passowrdField.getText().trim());
    crudInstance = DBHelper.instance(jdbcProp);
    if (schema == Schema.MYSQL) {
      mySQLDao = new MySQLDao(crudInstance);
    }
    if (schema == Schema.ORACLE) {
      oracleDao = new OracleDao(crudInstance);
    }

  }
}
